/* Moderator	: helpbygrace ( helpbygrace@gmail.com ) */
/*******************************************************
  CISS 정의파일.
  *****************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ciss.h"
#include "proc.h"
#include "mem.h"
#include "common.h"

/* DECLARE :: INTERNAL FUNCTIONS */
char *issgetline(char *line, int size);
char **tokenize(char *str, size_t *cnt);
void print_opt(int optc, char **opts);
void logo(void);
void view_gpr(proc_t *proc, mem_t *mem);
void view_spr(proc_t *proc, mem_t *mem);
void view_bps(proc_t *proc, mem_t *mem);
void view_wps(proc_t *proc, mem_t *mem);
void help(char * cmd);
/* END :: INTERNAL FUNCTIONS */
/* Prompt 표시 및 명령어 디코딩 */
int ciss(void)
{
  proc_t	*proc = (proc_t*)malloc(sizeof(proc_t));
  mem_t		*mem	= (mem_t*)malloc(sizeof(mem_t));
  int	idx;
  int	ret;
  char	*tok;
  char	buffer[256];
  char	**opts = NULL;
  size_t	optc;
  COMMAND_DECLARE((*command));

  init_proc(proc);
  init_mem(mem,1024);

  logo();
  while(1)
    {
      fputs(">> ", stdout);
      fflush(stdout);
      issgetline(buffer, sizeof(buffer));	/* 한줄 입력을 받는다 */

      opts = tokenize(buffer,&optc);
      if(optc == 0)
	{
	  continue;
	}

      /* cmds[]중에 맞는 token이 있는지 비교하여 실행한다 */
      for(idx = 0 ;cmds[idx].name != NULL;idx++)
	{
	  if(!strcmp(cmds[idx].name,opts[0]))
	    {
	      command = cmds[idx].function;
	      ret = command(proc,mem,optc,opts);
	      break;
	    }
	}

      for(idx = 0 ; idx < optc; idx++)
	free(opts[idx]);

      if(ret == QUIT)
	{
	  /* QUIT 일 경우, 할당된 공간을 해제하고 종료합니다. */
	  fini_proc(proc);
	  fini_mem(mem);
	  break;
	}
    }

  return SUCCESS;
}

/* Command Interpreter 정의 부분 */
COMMAND_FORMAT(cmd_run)
{
  fprintf(stdout,"cmd_run function.\n");
  print_opt(optc,opts);
  return 0;
}

COMMAND_FORMAT(cmd_continue)
{
  fprintf(stdout,"cmd_continue function.\n");
  print_opt(optc,opts);
  return 0;
}

COMMAND_FORMAT(cmd_step)
{

  fprintf(stdout,"cmd_step function.\n");
  print_opt(optc,opts);
  return 0;
}

/********************************************************************
  cmd_info
    proc의 내부 정보를 보는 명령어입니다.
    
    볼 수 있는 정보는 다음과 같습니다.
      1. 레지스터
          info regs
      2. PS
          info regs 에 같이 출력됨
      3. breakpoint 목록
          info bps
      4. watchpoint 목록
          info wps
********************************************************************/
void view_gpr(proc_t *proc, mem_t *mem)
{
  int idx;
  for(idx = 0 ; idx < GPR_SIZE ; idx++)
    {
      fprintf(stdout, "GPR[%02d]\t= 0x%08X",idx, GPR(proc,idx));
      if(idx % 4 == 3)
	{
	  fprintf(stdout,"\n");
	}
      else
	{
	  fprintf(stdout,"\t");
	}
    }
}
void view_spr(proc_t *proc, mem_t *mem)
{
  fprintf(stdout, "PC\t= 0x%08X\tPS\t= 0x%08X\n",SPR_PC(proc),SPR(proc,1));
}

void view_bps(proc_t *proc, mem_t *mem)
{
  int idx;

  for(idx = 0 ; idx < BP_SIZE ; idx++)
    {
      if(BPE(proc,idx))
	fprintf(stdout, "BP[%1d]\t= 0x%08X\n",idx, BVR(proc,idx));
    }

}

void view_wps(proc_t *proc, mem_t *mem)
{
  int idx;

  for(idx = 0 ; idx < WP_SIZE ; idx++)
    {
      if(WPE(proc,idx))
	fprintf(stdout, "WP[%1d]\t= 0x%08X\n",idx, WVR(proc,idx));
    }
}
COMMAND_FORMAT(cmd_info)
{
  int idx;
  if(!strcmp(opts[1],"regs"))
    {
      view_gpr(proc,mem);
      view_spr(proc,mem);
    }
  else if(!strcmp(opts[1], "bps"))
    {
      view_bps(proc,mem);
    }
  else if(!strcmp(opts[1],"wps"))
    {
      view_wps(proc,mem);
    }
  else
    {
      help("info");
    }
  return 0;
}

COMMAND_FORMAT(cmd_quit)
{

  fprintf(stdout,"cmd_quit function.\n");
  print_opt(optc,opts);
  return QUIT;
}

COMMAND_FORMAT(cmd_mem)
{

  fprintf(stdout,"cmd_mem function.\n");
  print_opt(optc,opts);
  return 0;
}

COMMAND_FORMAT(cmd_break)
{

  fprintf(stdout,"cmd_break function.\n");
  print_opt(optc,opts);
  return 0;
}

COMMAND_FORMAT(cmd_watch)
{

  fprintf(stdout,"cmd_watch function.\n");
  print_opt(optc,opts);
  return 0;
}

COMMAND_FORMAT(cmd_delete)
{

  fprintf(stdout,"cmd_delete function.\n");
  print_opt(optc,opts);
  return 0;
}


/********************************************************************
  INTERNAL FUNCTIONS
********************************************************************/
void logo(void)
{
  fprintf(stdout,"theCore Instruction-Set Simulator %s\n",CISS_VERSION);
  fprintf(stdout,"  by helpbygrace\n");
}
/* 안전하게 한줄 받아오기 */
char *issgetline(char *line, int size)
{
  if(fgets(line,size,stdin))
    {
      char *newline = strchr(line,'\n');
      if (newline)
	{
	  *newline = '\0';
	}
    }
  return line;
}

/* 공백으로 분리해서 string 배열로 넘겨주기 */
char **tokenize(char *str, size_t *cnt)
{
  char **ret = (char**)malloc(sizeof(char*)*10);
  char *tok = strtok(str," ");
  size_t	size = 0;
  size_t	len;
  while (tok != NULL)
    {
      len = strlen(tok);
      ret[size] = malloc(sizeof(char)*len);
      memcpy(ret[size],(void *)tok, len);
      size++;

      tok = strtok(NULL," ");
      if(size >= 10)
	break;
    }
  *cnt = size;
  if(size == 0)
    free(ret);

  return ret;
}

/* OPT 출력하기 */
void print_opt(int optc, char **opts)
{
  int idx;
  fprintf(stdout,"opt: ");
  for(idx = 0 ; idx < optc; idx++)
    fprintf(stdout, "%s, ",opts[idx]);
  fprintf(stdout,"\n");
}

void help(char * cmd)
{
  if(!strcmp(cmd,"info"))
    {
      fprintf(stdout, "Usage] info {regs|bps|wps}\n");
      fprintf(stdout, "  = Command =\n");
      fprintf(stdout, "\tregs : Show GPR, PC, and PS\n");
    }
}
